fix: use custom modulemap to prevent Xcode 26 ODR errors with CocoaPods#428
Open
mfazekas wants to merge 4 commits intorive-app:mainfrom
Open
fix: use custom modulemap to prevent Xcode 26 ODR errors with CocoaPods#428mfazekas wants to merge 4 commits intorive-app:mainfrom
mfazekas wants to merge 4 commits intorive-app:mainfrom
Conversation
The auto-generated modulemap includes a .Swift Clang submodule referencing RiveRuntime-Swift.h, which bakes in Swift stdlib C++ interop definitions for the exact Swift version used to build. When CocoaPods consumers use a different Xcode/Swift version, the differing type definitions cause hard ODR errors.
…bmodule
The Swift build phase unconditionally appends a `module RiveRuntime.Swift`
block to the framework's module.modulemap, referencing RiveRuntime-Swift.h.
This header bakes in Swift stdlib C++ interop type definitions (e.g.
swift::Optional::init parameter names) for the exact Swift version used to
build the XCFramework. When consumers use a different Xcode/Swift version,
these definitions conflict with the consumer's own Swift module definitions,
causing hard ODR errors that break the build.
The MODULEMAP_FILE build setting cannot prevent this: Xcode appends the
Swift submodule unconditionally after applying any custom modulemap.
Fix: post-process every module.modulemap in the assembled XCFramework to
strip the `module RiveRuntime.Swift { ... }` block. Without this entry,
Clang never loads the stale RiveRuntime-Swift.h into the module graph, so
the conflicting C++ type definitions are never seen.
This makes the shipped XCFramework compatible with any Xcode/Swift version
for both CocoaPods and SPM consumers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The custom modulemap file is no longer needed. The Xcode 26 ODR fix is handled entirely by the post-process step in build_framework.sh which strips the `module RiveRuntime.Swift` block from the assembled XCFramework after build. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When the XCFramework is built, the Swift build phase unconditionally appends a `module RiveRuntime.Swift` block to every `module.modulemap` slice, referencing `RiveRuntime-Swift.h`. This header bakes in Swift stdlib C++ interop type definitions (e.g. parameter names in `swift::Optional::init`) for the exact Swift/Xcode version used to build the XCFramework.
When consumers use a different Xcode/Swift version alongside a package that enables Swift/C++ interop (e.g. react-native-nitro-modules), both modules end up with conflicting definitions of the same C++ types in the same compilation unit, causing hard ODR errors:
```
error: 'swift::Optional' has different definitions in different modules;
definition in module 'RiveRuntime.Swift' found 1st parameter named 'some_' ← built with Xcode 16.4
but in 'NitroModules.Swift' found 1st parameter named 'value' ← built with Xcode 26
```
Why MODULEMAP_FILE alone doesn't work
Setting `MODULEMAP_FILE` in xcconfig provides a custom base modulemap, but Xcode's Swift build phase always appends the `module RiveRuntime.Swift { ... }` block afterward, regardless of what the custom modulemap contains. There is no build setting to prevent this.
Fix
Post-process every `module.modulemap` in the assembled XCFramework after `xcodebuild -create-xcframework` to strip the `module RiveRuntime.Swift { ... }` block. Without this entry, Clang never loads the stale `RiveRuntime-Swift.h` into the module graph, so the conflicting C++ type definitions are never visible to consumers.
This makes the shipped XCFramework compatible with any Xcode/Swift version, for both CocoaPods and SPM consumers (SPM downloads the already-patched artifact, so no consumer-side hooks are needed).
Options considered
post_installhook (consumer-side workaround)Testing
Verified with an Expo/React Native app using `@rive-app/react-native` + `react-native-nitro-modules` on Xcode 26: ODR errors are gone after the `post_install` hook applies the same strip. Zero `swift::Optional` / `swift::UTF8View` / lambda ODR errors in the build output.
See rive-app/rive-nitro-react-native#174